﻿using Castle.DynamicProxy;
using LiuQiuRong.Chat.Infrastructures.DataStore;
using LiuQiuRong.Chat.Infrastructures.Dependencies;
using LiuQiuRong.Chat.Infrastructures.Models;
using LiuQiuRong.Chat.Infrastructures.Options;
using Microsoft.Extensions.Options;
using SqlSugar;
using System;
using System.Linq;

namespace LiuQiuRong.Chat.Infrastructures.Interceptors
{
    /// <summary>
    /// 实现数据库工作单元拦截器，支持服务层、仓储层开启事务，支持嵌套事务
    /// </summary>
    public partial class TransactionInterceptor : IInterceptor
    {
        // 注入IDataAccessor 数据库访问器对象，数据库操作封装在其属性中
        private readonly IDataAccessor _dataAccessor;
        // 注入 ISqlSugarClient 数据库操作对象，真正的数据库CURD操作
        private readonly ISqlSugarClient _sqlsugarClient;
        // 注入选项Options操作对象，此处注入部署的选项对象
        private readonly DeployEnvironmentOptions _deployEnvironmentOptions;
        public TransactionInterceptor(
            IDataAccessor dataAccess,
            ISqlSugarClient sqlsugarClient,
            IOptionsMonitor<DeployEnvironmentOptions> optionsAccessor)
        {
            _dataAccessor = dataAccess;
            _sqlsugarClient = sqlsugarClient;
            _deployEnvironmentOptions = optionsAccessor.CurrentValue;
        }

        public void Intercept(IInvocation invocation)
        {
            // 如果数据库已经开启，直接跳过并继续执行方法
            if (!string.IsNullOrEmpty(_dataAccessor.StartPoint))
            {
                invocation.Proceed();
                return;
            }

            var method = invocation.MethodInvocationTarget ?? invocation.Method;    // 获取当前执行的方法对象
            var reflectedType = method.ReflectedType;   // 获取当前方法所在的类类型

            // 如果是抽象类或者不是IServiceDependency和IRepositoryDependency实现类直接跳过并继续执行方法
            if (reflectedType.IsAbstract
                || (!typeof(IService).IsAssignableFrom(reflectedType) && !typeof(IRepository).IsAssignableFrom(reflectedType)))
            {
                invocation.Proceed();   // 继续执行
                return;
            }

            var transactionAttribute = method.GetCustomAttributes(typeof(TransactionAttribute), true).FirstOrDefault() as TransactionAttribute;  // 获取方法是否贴有 [Transaction] 日志特性
            // 如果服务层或仓储层方法贴有 [NonConnection] 特性，或者服务层未贴有 [Transaction] 特性直接跳过并继续执行方法
            if (method.GetCustomAttributes(typeof(NonConnectionAttribute), true).FirstOrDefault() is NonConnectionAttribute
                || (transactionAttribute == null && typeof(IService).IsAssignableFrom(reflectedType)))
            {
                invocation.Proceed();   // 继续执行
                return;
            }

            try
            {
                // 如果数据库操作对象为空
                if (_dataAccessor.DbContext == null)
                {
                    _sqlsugarClient.CurrentConnectionConfig.ConnectionString = Initialize.ConnectionStringKey;  // 设置连接字符串

                    _dataAccessor.DbContext = _sqlsugarClient;  // 将注入的数据库操作对象赋值给数据访问器DbContext属性
                    _dataAccessor.StartPoint = $"{reflectedType.FullName}.{method.Name}";   // 记录当前开启数据库连接的方法全名称
                }

                // 如果 贴有 [Transaction] 特性，则开启事务
                if (transactionAttribute != null) _dataAccessor.DbContext.Ado.BeginTran();
                invocation.Proceed();   // 继续执行
                // 关闭之前开启的事务
                if (transactionAttribute != null) _dataAccessor.DbContext.Ado.CommitTran();
            }
            catch (Exception ex)
            {
                // 如果执行出现异常，则回滚事务
                if (transactionAttribute != null) _dataAccessor.DbContext.Ado.RollbackTran();
                throw ex;   // 抛出异常
            }
            finally
            {
                _dataAccessor.StartPoint = null;    // 清空记录点
                _dataAccessor.DbContext.Close();    // 关闭数据库连接
            }
        }
    }
}
